/**
 * utility
 *
 * Provides helper functions to communicate 
 * with the LinkSprite JPEG Digital Camera
 * utilized in server.c
 *
 * @author Team Wire-Lettuce
 * @date 12 Apr 2011
 */

#include "camera.h"



// Command Values ending with 0xFF to determine size
char cmdReset[5] = {0x56, 0x00, 0x26, 0x00, 0xFF};
char cmdTakePic[6] = {0x56, 0x00, 0x36, 0x01, 0x00, 0xFF};
char cmdReadFileSize[6] = {0x56, 0x00, 0x34, 0x01, 0x00, 0xFF};
char cmdReadPic[13] = {0x56, 0x00, 0x32, 0x0C, 0x00, 0x0A, 0x00, 0x00, 0x00>>8, 0x00, 0x00, 0x00, 0xFF};
char cmdStopTakingPics[6] = {0x56, 0x00, 0x36, 0x01, 0x03, 0xFF};


// Return Values ending with 0xFF to determine size
char returnReset[5] = {0x76, 0x00, 0x26, 0x00, 0xFF};
char returnTakePic[6] = {0x76, 0x00, 0x36, 0x00, 0x00, 0xFF};
char returnReadFileSize[8] = {0x76, 0x00, 0x34, 0x00, 0x04, 0x00, 0x00, 0xFF};
char returnReadPic[6] = {0x76, 0x00, 0x32, 0x00, 0x00, 0xFF};
char returnStopTakingPics[6] = {0x76, 0x00, 0x36, 0x00, 0x00, 0xFF};

// This global file descriptor, once initialized,
// keeps track of the serial port opened to the GSM Module.
// It is used only by this file for easier port
// management.

int fd = -1;


/**
 * resetCamera()
 *
 * Sends the command to reset the Camera
 *
 * @return Error if bytes are not received or the proper 
 *           response is not received
 *         Success if reset was successful
 */
int resetCamera()
{
  int i;
  char buf[1];

  // send command
  byteTx(cmdReset);

  sleep(1);

  // check for proper response from camera
  for(i = 0; i < sizeOfArray(returnReset); i++)
    {
      if(byteRx(buf)==ERROR)
	return ERROR;

      if(buf[0] != returnReset[i])
	{
	  perror("ERR: resetCamera() return val\n");
	  return ERROR;
	}
    }
  return SUCCESS;
}//resetCamera()


/**
 * waitInit()
 *
 * Waits for the camera to complete initializing
 *
 * @return Error if bytes are not received 
 *            or the proper response is not received
 *         Success when the correct bytes 
 *            were received signifying the end of initialization
 */
int waitInit()
{
  // wait for camera to send finished initialization message
  int RCV_state = TRUE;
  int OD_state = FALSE;
  int is64 = FALSE;
  char buf[1];

  // continue receiving until we receive the end code
  while(RCV_state == TRUE)
    {
      if(byteRx(buf)==ERROR)
	return ERROR;

      // check for end code (64 0d 0a)
      if(buf[0] == 0x64)
	{
	  is64 = TRUE;
	}
      else if (is64 == TRUE && buf[0] == 0x0d)
	{
	  OD_state = TRUE;
	}
      else if (is64 == TRUE && OD_state == TRUE
	       && buf[0] == 0x0a)
	{
	  RCV_state = FALSE;
	}
      else
	{
	  is64 = FALSE;
	  OD_state = FALSE;
	  RCV_state = TRUE;
	}
    }//while
  

  return SUCCESS;
}//waitInit()
 

/**
 * takePicture()
 *
 * Sends the command to take a picture to the camera
 *
 * @return Error if bytes are not received 
 *           or the proper response is not received
 *         Success when the command is successfully sent
 */  
int takePicture()
{
  int i;
  char buf[1];
  
  // send command
  byteTx(cmdTakePic);

  sleep(1);

  // check for proper response from camera
  for(i = 0; i < sizeOfArray(returnTakePic); i++)
    {
      if(byteRx(buf)==ERROR)
	  return ERROR;

      if(buf[0] != returnTakePic[i])
	{
	  perror("ERR: returnTakePic() return val\n");
	  return ERROR;
	}
    }

  return SUCCESS;
}//takePicture


/**
 * readFileSize()
 *
 * Sends the command to get the file size of the image and converts this to an integer
 *
 * @return Error if bytes are not received 
 *           or the proper response is not received
 *         Success when the command is successfully sent
 */    
int readFileSize()
{
  int i,size;
  char buf[1];

  // send command
  byteTx(cmdReadFileSize);

  sleep(1);

  // check for proper response from camera
  for(i = 0; i < sizeOfArray(returnReadFileSize); i++)
    {
      if(byteRx(buf)==ERROR)
	return ERROR;

      if(buf[0] != returnReadFileSize[i])
	{
	  perror("ERR: returnReadFileSize() return val\n");
	  return ERROR;
	}
    }
  
  // receive high bytes of size
  if(byteRx(buf)==ERROR)
	return ERROR;

  size = (int)buf[0]*256;

  // receive low bytes of size
  if(byteRx(buf)==ERROR)
	return ERROR;

  size += (int)buf[0] & 0x00FF; 

  return size;
}//readFileSize

   
/**
 * readPicture()
 *
 * Sends the command to get the image of a 
 * specified size and stores it into picBuf
 *
 * @arg picBuf pointer to a character array 
 *        to store the picture data into
 *      size size of picture to receive
 *
 * @return Error if bytes are not received 
 *           or the proper response is not received
 *         Success when the command is successfully sent
 */     
int readPicture(char* picBuf, int size)
{
  int i;
  char buffer[5] = {size>>8, size, 0x00, 0x0A, 0xFF};
  char buf[1];
  
  // send command
  byteTx(cmdReadPic);
  byteTx(buffer);

  
  sleep(1);

  // check for proper response
  for(i = 0; i < sizeOfArray(returnReadPic); i++)
    {
      if(byteRx(buf)== ERROR)
	{
	  perror("ERR: byteRx\n");
	  return ERROR;
	}
	  
      if(buf[0] != returnReadPic[i])
	{
	  
	  perror("ERR: returnReadPic() return val\n");
	  return ERROR;
	}
    }

  // read the picture data from the camera and store into picBuf
  if((read(fd, picBuf, size)) == ERROR)
    {
      perror("ERR: rxPic() reading\n");
      return ERROR;
    }
  
  // check for second part of response
  for(i = 0; i < sizeOfArray(returnReadPic); i++)
    {
      if(byteRx(buf)== ERROR)
	return ERROR;
	  
      if(buf[0] != returnReadPic[i])
	{
	  perror("ERR: returnReadPic2() return val\n");
	  return ERROR;
	}
    }

  return SUCCESS;
}//readPicture

    
/**
 * stopTakingPictures()
 *
 * Sends the command to the camera 
 * to stop taking pictures 
 *
 * @return Error if bytes are not received 
 *           or the proper response is not received
 *         Success when the correct bytes 
 *           were received signifying the end of initialization
 */   
int stopTakingPictures()
{
  int i;
  char buf[1];
  
  // send command
  byteTx(cmdStopTakingPics);

  sleep(1);

  // check for proper response
  for(i = 0; i < sizeOfArray(returnStopTakingPics); i++)
    {
      if(byteRx(buf)==ERROR)
	return ERROR;

      if(buf[0] != returnStopTakingPics[i])
	{
	  perror("ERR: returnStopTakingPics() return val\n");
	  return ERROR;
	}
    }

  return SUCCESS;
}//stopTakingPictures

     
/**
 * byteTx()
 *
 * Transmits a command to the camera over the serial interface
 * denoted by fd.
 *
 * @arg buffer the char array of the command to send
 *
 * @return void
 */
void byteTx(char* buffer)
{
  char buf[1];  // temporary buffer to write to camera

  int sizeOfBuffer = sizeOfArray(buffer);  // size of the buffer  
  int i;

  // write the command to the camera
  for(i = 0; i < sizeOfBuffer; i++)
    {
      buf[0] = buffer[i];
      write(fd, buf, 1);
    }
}//byteTx

/**
 * openPort()
 *
 * Opens ttyS2 port to write to camera.
 * 
 * @return -1 if port failed to open and 0 if port successfuly opens
 */
int openPort()
{
  // ttyS2 denotes the port the camera is connected
  fd = open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NDELAY);

  if (!fd)
    {
      perror("ERR: openPort() serial failed to open \n");
      return ERROR;
    }
  
  return fd;
}
  
    
/**
 * closePort()
 * 
 * Closes serial port to the camera.
 *
 * @return 1 if successful, -1 if failed.
 */
int closePort()
{
  if(!close(fd))
    {
      return SUCCESS;
    }

  else
    {
      perror("ERR: closePort() serial failed to close \n");
      return ERROR;
    }
}


/**
 * byteRx()
 *
 * Reads a byte from the camera over the serial interface.
 *
 * @arg buffer a character pointer to the buffer which will
 *      store the received data.
 *
 * @return int - ERROR/bytesRead
 */
int byteRx(char* buffer)
{
  if((read(fd, buffer, 1)) == ERROR)
    {
      perror("ERR: byteRx() reading\n");
      return ERROR;
    }
  return SUCCESS;

  }

/**
 * sizeOfArray()
 *
 * Helper function returns the size of a given array
 *
 * @arg buffer character pointer to buffer
 *
 * @return int size of buffer
 *
 */
int sizeOfArray(char* buffer)
{
  int count = 0;

  // while the end of the buffer has not been reached
  while(buffer[count] != 0xFF)
    {
      count++;
    }
  return count;
}
